// CVisCommandLineInterface.cpp
// Developed by Yang Yu (yuy@janelia.hhmi.org), Sept 25, 2012
// 

#include "CVisCommandLineInterface.h"

// check file format
bool check_fileformat(char *filename, char *extension)
{
    
    if(filename == NULL || extension == NULL)
        return false;
    
    if(strlen(filename) == 0 || strlen(extension) == 0)
        return false;
    
    if(strchr(filename, '.') == NULL || strchr(extension, '.') == NULL)
        return false;
    
    for(int i = 0; i < strlen(filename); i++)
    {
        if(tolower(filename[strlen(filename) - i - 1]) == tolower(extension[strlen(extension) - i - 1]))
        {
            if(i == strlen(extension) - 1)
                return true;
        } else
            break;
    }
    
    return false;
}

// Command Line Interface
CVisCommandLineInterface::CVisCommandLineInterface()
{
    funcname = NULL;
    inputsList.clear();
    parametersList.clear();
    outputsList.clear();
}

CVisCommandLineInterface::~CVisCommandLineInterface()
{
}

// Command Line Parser
CVisCommandLineParser::CVisCommandLineParser()
{
}

CVisCommandLineParser::~CVisCommandLineParser()
{
}

int CVisCommandLineParser::error( void (*help)() )
{
    cout<<"Illegal Inputs."<<endl;
    help();
    
    return -1;
}

bool CVisCommandLineParser::check_filename(char* filename)
{
    path pFile(filename);
    
    try {
        if(!exists(pFile)) 
        {
            cout<<"Invalid file or directory!"<<endl;
            return false;
        }
        else
        {
            if(is_regular_file(pFile)) // file
            {
                char tiffext[] = ".tif";
                char niftiext[] = ".nii";
                char niftigzext[] = ".nii.gz";
                
                if( check_fileformat(filename, tiffext) || check_fileformat(filename, niftiext) || check_fileformat(filename, niftigzext))
                {
                    return true;
                }
                else 
                {
                    cout<<"Unsupported input"<<endl;
                    return false;
                }
                
            }
            else if(is_directory(pFile)) // directory
            {
                //
            }
        }
    } catch (const filesystem_error& ex) {
        cout << ex.what() << '\n';
    }
    
    return true;
    
}

int CVisCommandLineParser::parse(int argc, char *argv[], void (*help)(), void(*version)())
{
    if (argc<=1)
    {
        // 
        help();
    }
    else
    {
        // read from configuration file
        vector<char *> parList; 
        
        // command arguments parsing
        char* key;
        
        //
        if(argc<=2)
        {
            key = argv[1];
            if (*key == OPTION_CHAR)
            {
                while(*++key)
                {
                    if (*key == '?' || !strcmp(key, "h") || !strcmp(key, "H"))
                    {
                        help();
                    }
                    else if (string(argv[1]).find("-psn_") == 0) {
                        cout<<"Mac bundle "<<argv[1]<<endl;
                        return 0;
                    }
                    else if(!strcmp(key, "v"))
                    {
                        version();
                        return 0;
                    }
                    else
                    {
                        cout<<"Invalid command"<<endl;
                        return -3;
                    }
                }
                
            }
            else if( check_filename(key) )
            {
                // load and visualize file in V3D
                char *filename = argv[1];
                cli.inputsList.push_back(filename);                
            }
            else
            {
                return error(help);
            }
        }
        else
        {
            // version
            for(int i=1; i<argc; i++)
            {
                key = argv[i];
                if (*key == OPTION_CHAR)
                {
                    while(*++key)
                    {
                        if (!strcmp(key, "v"))
                        {
                            version();
                            return 0;
                        }
                    }
                }
            }
            
            // find -h and -exec combination
            int flagh = 0, flagx = 0;
            for(int i=1; i<argc; i++)
            {
                key = argv[i];
                if (*key == OPTION_CHAR)
                {
                    while(*++key)
                    {
                        if (*key == '?' || !strcmp(key, "h") || !strcmp(key, "H"))
                        {
                            flagh++;
                        }
                    }
                }
                key = argv[i];
                if(*key == OPTION_CHAR)
                {
                    while (*++key)
                    {
                        if(!strcmp(key, "exec"))
                        {
                            key+=3;; // skip "exec"
                            
                            flagx++;
                            
                            if(i+1>=argc) return error(help);
                            
                            // func
                            cli.funcname = argv[i+1];
                        }
                    }
                }
                
                if(flagh && flagx)
                {
                    return true;
                }
            }
   
            // other cases
            for(int i=1; i<argc; i++)
            {
                if(i+1 != argc) // check that we haven't finished parsing yet
                {
                    
                    key = argv[i];
                    
                    if (*key == OPTION_CHAR)
                    {
                        while(*++key)
                        {
                            if (!strcmp(key, "i"))
                            {
                                
                                while(i+1<argc && !*(string(argv[i+1]).begin()) != OPTION_CHAR )
                                {
                                    char *filename = argv[i+1];
                                    
                                    if (*filename == OPTION_CHAR) break;
                                    
                                    if( check_filename(filename) ) //
                                    {
                                        cli.inputsList.push_back(filename);
                                        i++;
                                    }
                                    else
                                    {
                                        cout << "Unsupported files!"<<endl;
                                        return -2;
                                    }  
                                }
                            }
                            else if (!strcmp(key, "o"))
                            {
                                while(i+1<argc && !*(string(argv[i+1]).begin()) != OPTION_CHAR )
                                {
                                    char *filename = argv[i+1];
                                    
                                    if (*filename == OPTION_CHAR) break;
                                    
                                    cli.outputsList.push_back(filename);
                                    i++;
                                    
//                                    if( check_filename(filename) ) //
//                                    {
//                                        cli.outputsList.push_back(filename);
//                                        i++;
//                                    }
//                                    else
//                                    {
//                                        cout << "Unsupported files!"<<endl;
//                                        return -2;
//                                    }
                                }
                            }
                            else if (!strcmp(key, "exec"))
                            { 
                                key+=3;; // skip "exec"
                                
                                // func
                                cli.funcname = argv[i+1];
                                i++;
                            }
                            else
                            {
                                cout<<"Invalid commands. Please check 'cvis -h'"<<endl;
                                return error(help);
                            } 
                        }
                    }
                    else if(!strcmp(key, "p"))
                    {
                        // function parameters
                        while(i+1<argc && *(string(argv[i+1]).begin()) != OPTION_CHAR )
                        {
                            char *strparameters = argv[i+1];
                            
                            cli.parametersList.push_back(strparameters);
                            i++;
                        }
                    }
                    else if(!strcmp(key, "pf"))
                    {
                        key++; // skip "pf"
                        
                        // function parameters from configuration file
                        if(i+1<argc)
                        {
                            char *fn = argv[i+1];
                            std::ifstream pConfigFile(fn);
                            
                            string str;
                            
                            if(pConfigFile.is_open())
                            {
                                while( !pConfigFile.eof() )
                                {
                                    while( getline(pConfigFile, str) )
                                    {
                                        parList.push_back((char *)(str.c_str()));
                                        
                                    }
                                }
                            }
                            else
                            {
                                cout << "Unable to open the configure file"<<endl;
                                return -2;
                            }
                            
                            pConfigFile.close(); 
                        }
                        i++;
                        
                    }
                    else
                    {
                        return error(help);
                    }
                    
                }
                
            }
            
            //
            for(int i=0; i<parList.size(); i++)
            {
                cli.parametersList.push_back(parList.at(i));
            }
            
        }
    }
    
    return true;
}
